using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace net.notdot.Attercop.Client
{
    public partial class ServerWindow : Form
    {
        public const string ChatMessageFormat = "<{0}> {1}";
        public const string EmoteMessageFormat = "*{0} {1}";
        public const string NoticeFormat = "*** {0}";

        private MessageAddressing target;

        public ClientInstance ClientInstance;

        private Dictionary<Peer, PMWindow> PMs = new Dictionary<Peer, PMWindow>();

        public ServerWindow(ClientInstance c)
        {
            ClientInstance = c;
            
            c.ChatEvent += new EventHandler<MessageEventArgs<ChatMessage>>(c_ChatEvent);
            c.PeerJoin += new EventHandler<PeerEventArgs>(c_PeerJoin);
            c.PeerQuit += new EventHandler<PeerEventArgs>(c_PeerQuit);
            c.SearchHandler = new ClientInstance.SearchDelegate(c_SearchHandler);

            target = new MessageAddressing(c.LocalPeer.Address, c.NetworkSettings.NetworkAddress);

            InitializeComponent();

            this.Text = string.Format("Connected to {0}", c.NetworkSettings.NetworkAddress.ToString());

            c.SendMessage(new HelloMessage(new MessageAddressing(c.LocalPeer.Address, c.NetworkSettings.NetworkAddress), c.LocalPeer));
        }

        private class SearchFilter
        {
            SearchMessage Message;

            public SearchFilter(SearchMessage message)
            {
                this.Message = message;
            }

            public bool Filter(PathNode nd) {
                return (Message.MinSize == -1 || (!nd.IsDir && nd.TotalSize >= Message.MinSize))
                    && (Message.MaxSize == -1 || (!nd.IsDir && nd.TotalSize <= Message.MaxSize))
                    && (Message.MimeType == "" || nd.MimeType.StartsWith(Message.MimeType.ToLower()))
                    && (!Message.IsDir.HasValue || Message.IsDir == nd.IsDir);
            }
        }

        IList<SearchResult> c_SearchHandler(SearchMessage m)
        {
            UserSettings user = (UserSettings)ClientInstance.UserSettings;
            List<PathNode> results = user.TreeBuilder.Index.Search(m.Keywords, new Predicate<PathNode>(new SearchFilter(m).Filter));
            if (results.Count == 0)
                return null;

            SearchResult[] ret = new SearchResult[results.Count];
            for (int i = 0; i < results.Count; i++)
            {
                ret[i] = new SearchResult(results[i].FullName, results[i].TotalSize, results[i].Hash, results[i].IsDir);
            }

            return ret;
        }

        void c_PeerQuit(object sender, PeerEventArgs e)
        {
            this.Invoke(new VoidDelegate(delegate()
            {
                if(PMs.ContainsKey(e.Peer))
                    AddLine(e.Peer, string.Format(NoticeFormat, e.Peer.Nick + " has disconnected."));

                peerBindingSource.Remove(e.Peer);
            }));
        }

        void c_PeerJoin(object sender, PeerEventArgs e)
        {
            this.Invoke(new VoidDelegate(delegate()
            {
                if(PMs.ContainsKey(e.Peer))
                    AddLine(e.Peer, string.Format(NoticeFormat, e.Peer.Nick + "has connected."));

                peerBindingSource.Add(e.Peer);
            }));
        }

        void c_ChatEvent(object sender, MessageEventArgs<ChatMessage> e)
        {            
            this.Invoke(new VoidDelegate(delegate()
            {
                string message = string.Format(e.Message.IsEmote ? EmoteMessageFormat : ChatMessageFormat, e.Message.Sender.Nick, e.Message.Text, System.Environment.NewLine);

                if(e.Message.Addressing.To.Equals(ClientInstance.NetworkSettings.NetworkAddress)) {
                    //Multicast message
                    AddLine(message);
                } else {
                    //Personal message
                    AddLine(e.Message.Sender, message);
                }
            }));
        }

        private void txtChatEntry_KeyUp(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Enter:
                    CommandParser.Command cmd = CommandParser.Parse(txtChatEntry.Text);
                    switch (cmd.Type)
                    {
                        case CommandParser.CommandType.Invalid:
                            AddLine(string.Format(NoticeFormat, "Command not recognised"));
                            break;
                        case CommandParser.CommandType.ChatMessage:
                            ClientInstance.SendMessage(new ChatMessage(target, cmd.Parameters[0]));
                            break;
                        case CommandParser.CommandType.Emote:
                            ClientInstance.SendMessage(new ChatMessage(target, cmd.Parameters[0], true));
                            break;
                    }

                    txtChatEntry.Text = "";
                    break;
            }
        }

        public void AddLine(string s) {
            txtChatbox.Text += s + Environment.NewLine;
        }

        public void AddLine(Peer p, string s)
        {
            PMWindow win = null;

            if (PMs.ContainsKey(p))
            {
                win = PMs[p];
            }
            else
            {
                win = new PMWindow(ClientInstance, p);
                win.MdiParent = this.MdiParent;
                win.Show();
                PMs.Add(p, win);
            }

            win.AddLine(s);
        }

        private void OpenPMWindow(object sender, EventArgs e)
        {
            Peer p = (Peer)lstUsers.SelectedItem;
            if(!PMs.ContainsKey(p)) {
                PMWindow win = new PMWindow(ClientInstance, p);
                win.MdiParent = this.MdiParent;
                win.Show();
                PMs.Add(p, win);
            } else {
                PMs[p].BringToFront();
            }
        }
    }
}